﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using NymphsLib.CommonUtilities.Components;
using NymphsLib.CommonUtilities.Extensions;
using MovieBrowser.Controller;
using MovieBrowser.Event;
using MovieBrowser.Model;

namespace MovieBrowser.Presentation.Forms.Db
{
    public partial class CleanUpDatabase : Form
    {
        private readonly MovieBrowserController _controller;

        public CleanUpDatabase()
        {
            InitializeComponent();
            _controller = new MovieBrowserController();
        }

        public CleanUpDatabase(MovieBrowserController entities)
        {
            InitializeComponent();
            _controller = entities;
        }

        private void ClFixUpdateClick(object sender, EventArgs e)
        {
            var i = new DbCleanUp { MovieController = _controller };
            i.OnDebugText += DebugTextFired;
            var t = new Thread(i.FixUpdatedBit);
            t.Start();
        }

        private void ClFixKeywordsClick(object sender, EventArgs e)
        {
            var i = new DbCleanUp { MovieController = _controller };
            i.OnDebugText += DebugTextFired;
            var t = new Thread(i.FixKeywords);
            t.Start();

        }

        private void ClFixStarsClick(object sender, EventArgs e)
        {
            var info = new DbCleanUp { MovieController = _controller };
            info.OnDebugText += DebugTextFired;
            var thread = new Thread(info.CleanupReferences);
            thread.Start();
        }

        private void CommandLink1Click(object sender, EventArgs e)
        {
            var info = new DbCleanUp { MovieController = _controller };
            info.OnDebugText += DebugTextFired;
            var thread = new Thread(info.FixPersons);
            thread.Start();
        }

        private void clCleanLocation_Click(object sender, EventArgs e)
        {
            var filesystems = _controller.Db.MovieFilesystems;
            foreach (var movieFilesystem in filesystems)
            {
                _controller.Db.DeleteObject(movieFilesystem);
            }
            _controller.Db.SaveChanges();
        }

        private void ClFixCreationDateClick(object sender, EventArgs e)
        {
            var info = new DbCleanUp() { MovieController = _controller };
            info.OnDebugText += DebugTextFired;
            var thread = new Thread(info.FixCreationDate);
            thread.Start();
        }

        private void clPersonWorks_Click(object sender, EventArgs e)
        {
            var info = new DbCleanUp() { MovieController = _controller };
            info.OnDebugText += DebugTextFired;
            var thread = new Thread(info.FixPersonWorks);
            thread.Start();
        }


        private void cbFixStarActors_Click(object sender, EventArgs e)
        {
            var information = new DbCleanUp
            {
                MovieController = _controller,
            };
            information.OnDebugText += DebugTextFired;
            var thread = new Thread(information.CleanStarActors);
            thread.Start();
        }

        private void DebugTextFired(object sender, EventArgs e)
        {
            try
            {
                if (debugText.InvokeRequired)
                {
                    debugText.Invoke(new EventHandler(DebugTextFired), sender, e);
                }
                else
                {
                    var ee = e as DebugTextEventArgs;
                    if (ee != null)
                        if (ee.Data != null)
                        {
                            debugText.InsertTextAsRtf(e.ToString(), debugText.Font, RtfColor.Red);
                        }
                        else
                        {
                            debugText.AppendText(ee.ToString());
                        }
                }
            }
            catch
            {
            }
        }

        private class DbCleanUp
        {
            private MovieBrowserController _controller;
            public void FixUpdatedBit()
            {
                var watch = new Stopwatch();
                watch.Start();
                var list = from o in _controller.Db.Movies
                           where _controller.Db.MovieKeywords.Where(k => k.Movie.Id == o.Id).Count() == 0
                           select o;

                foreach (var movie in list)
                {
                    movie.IsUpdated = false;
                }
                _controller.Db.SaveChanges();
                watch.Stop();
                InvokeOnDebugText(string.Format("Update Checked."));
            }


            public MovieBrowserController MovieController
            {
                private get { return _controller; }
                set { _controller = value; }
            }

            public event EventHandler OnDebugText;
            public void InvokeOnDebugText(Exception exception)
            {
                if (OnDebugText != null) OnDebugText(this, new DebugTextEventArgs(exception));
            }
            public void InvokeOnDebugText(string text)
            {
                if (OnDebugText != null) OnDebugText(this, new DebugTextEventArgs(text));
            }
            //
            public void CleanStarActors()
            {
                foreach (var movie in MovieController.Db.Movies)
                {
                    foreach (var star in MovieController.Db.Stars.Where(o => o.Movie.Id == movie.Id).Select(o => o.Person))
                    {
                        bool found = false;
                        foreach (var actor in MovieController.Db.Actors.Where(o => o.Movie.Id == movie.Id).Select(o => o.Person))
                        {
                            if (actor.Id == star.Id)
                                found = true;
                        }

                        if (!found)
                        {
                            InvokeOnDebugText("Movie " + movie.Title + ", Actor " + star.Name);
                            var actor = new Actor { Person = star, Movie = movie };

                            MovieController.Db.AddToActors(actor);
                            MovieController.Db.SaveChanges();
                        }

                    }
                }
            }
            //
            public void FixCreationDate()
            {
                var watch = new Stopwatch();
                watch.Start();
                var movies = _controller.Db.Movies;
                foreach (var movie in movies)
                {
                    movie.CreationDate = movie.CreationDate;
                    _controller.Db.SaveChanges();
                }
                InvokeOnDebugText(string.Format("Creation Date Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            //
            public void FixPersonWorks()
            {
                foreach (Person person in MovieController.Db.People)
                {
                    MovieController.UpdatePersonWorks(person);
                    InvokeOnDebugText(string.Format("Person: {0}, Acted={1}, Directed={2}, Written={3}, Starred={4}", person.Name, person.WorksAsActor, person.WorksAsDirector, person.WorksAsWriter, person.Count1));
                }
                MovieController.Db.SaveChanges();
            }
            //

            public void CleanupReferences()
            {
                FixStars();
                FixDirectors();
                FixWriters();
                FixCountries();
                FixLanguages();
                FixGenres();

            }
            private void FixGenres()
            {
                var watch = new Stopwatch();
                watch.Start();
                var stars = _controller.Db.MovieGenres.ToList();
                foreach (var star in stars)
                {
                    if (star.Movie == null)
                    {
                        _controller.Db.DeleteObject(star);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Genres Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            private void FixLanguages()
            {
                var watch = new Stopwatch();
                watch.Start();
                var stars = _controller.Db.MovieLanguages.ToList();
                foreach (var star in stars)
                {
                    if (star.Movie == null)
                    {
                        _controller.Db.DeleteObject(star);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Languages Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            private void FixCountries()
            {
                var watch = new Stopwatch();
                watch.Start();
                var stars = _controller.Db.MovieCountries.ToList();
                foreach (var star in stars)
                {
                    if (star.Movie == null)
                    {
                        _controller.Db.DeleteObject(star);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Countries Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            private void FixStars()
            {
                var watch = new Stopwatch();
                watch.Start();
                var stars = _controller.Db.Stars.ToList();
                foreach (var star in stars)
                {
                    if (star.Movie == null)
                    {
                        _controller.Db.DeleteObject(star);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Stars Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            private void FixDirectors()
            {
                var watch = new Stopwatch();
                watch.Start();

                var directors = _controller.Db.Directors.ToList();

                foreach (var director in directors)
                {
                    if (director.Movie == null)
                    {
                        _controller.Db.DeleteObject(director);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Directors Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            private void FixWriters()
            {
                var watch = new Stopwatch();
                watch.Start();

                var writers = _controller.Db.Writers.ToList();

                foreach (var writer in writers)
                {
                    if (writer.Movie == null)
                    {
                        _controller.Db.DeleteObject(writer);
                        _controller.Db.SaveChanges();
                    }
                }
                InvokeOnDebugText(string.Format("Writers Clean Up Completed. Time elapsed: {0}", watch.Elapsed));
                watch.Stop();
            }
            //
            public void FixKeywords()
            {
                var watch = new Stopwatch();
                watch.Start();

                InvokeOnDebugText("Finding Duplicates in MovieKeywords.");
                foreach (var movie in _controller.Db.Movies)
                {
                    var duplicates1 = (from p in _controller.Db.MovieKeywords
                                       where p.Movie.Id == movie.Id
                                       group p by p.Keyword.Name into g
                                       select new { Name = g.Key, Count = g.Count() }).Where(o => o.Count > 1).ToList();
                    if (duplicates1.Count > 0)
                        InvokeOnDebugText(duplicates1.Count + " duplicates found.");

                    foreach (var dup in duplicates1)
                    {
                        var tt = (from p in _controller.Db.MovieKeywords
                                  where p.Movie.Id == movie.Id && p.Keyword.Name == dup.Name
                                  select new { KW = p.Keyword, ID = p.Id, MK = p }).ToList();

                        for (int i = 1; i < tt.Count; i++)
                        {
                            _controller.Db.DeleteObject(tt[i].MK);
                            _controller.Db.SaveChanges();
                            InvokeOnDebugText(tt[i].KW.Name + "");
                        }
                    }
                }

                InvokeOnDebugText("Finding Duplicates in Keywords.");
                var duplicates = (from p in _controller.Db.Keywords
                                  group p by p.Name into g
                                  select new { Name = g.Key, Count = g.Count() }).Where(o => o.Count > 1).ToList();
                InvokeOnDebugText(duplicates.Count + " duplicates found.");
                //Correct Keyword Duplicates
                foreach (var duplicate in duplicates)
                {
                    var duplicate1 = duplicate;
                    var keywords = (from keyword in _controller.Db.Keywords
                                    where keyword.Name == duplicate1.Name
                                    orderby keyword.Id
                                    select keyword).ToArray();

                    var firstKeyword = keywords[0];

                    InvokeOnDebugText("Removing Duplicate keyword " + firstKeyword);
                    for (var i = 1; i < keywords.Length; i++)
                    {
                        var curkey = keywords[i];
                        var mk = _controller.Db.MovieKeywords.Where(o => o.Keyword.Id == curkey.Id).ToList();

                        foreach (var movieKeyword in mk)
                        {
                            movieKeyword.Keyword = firstKeyword;
                        }

                        _controller.Db.DeleteObject(keywords[i]);
                    }
                }
                _controller.Db.SaveChanges();

                //Correct Movie Keywords Assignment
                InvokeOnDebugText("Fixing Orphaned Keywords.");
                var duplicateMovieKeywords = _controller.Db.MovieKeywords.GroupBy(i => new { MovieId = i.Movie.Id, KeywordId = i.Keyword.Id }).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
                foreach (var duplicateMovieKeyword in duplicateMovieKeywords)
                {
                    var keyword = duplicateMovieKeyword;
                    var mkws = (from o in _controller.Db.MovieKeywords
                                where o.Keyword.Id == keyword.KeywordId && o.Movie.Id == keyword.MovieId
                                select o).ToList();
                    for (var i = 1; i < mkws.Count; i++)
                    {
                        _controller.Db.DeleteObject(mkws[i]);
                    }
                }
                _controller.Db.SaveChanges();

                watch.Stop();
                InvokeOnDebugText(string.Format("Clean Up Completed. Time elapsed: {0}\r\nDuplicates Keywords Removed: {1}", watch.Elapsed, duplicates.Count));
            }
            public void FixPersons()
            {
                var persons = _controller.Db.People;
                foreach (var person in persons)
                {
                    if (string.IsNullOrEmpty(person.Name))
                    {
                        InvokeOnDebugText(string.Format("Empty Person Name: {0}\r\n", person.Name));
                    }
                    person.Name = person.Name.Clean();
                }
                _controller.Db.SaveChanges();

                var actors = _controller.Db.Actors;
                foreach (var person in actors)
                {
                    person.CharacterName = person.CharacterName.Clean();
                }
                _controller.Db.SaveChanges();


                //Find Duplicate
                var duplicates1 = (from p in _controller.Db.People
                                   group p by p.Name into g
                                   select new { Name = g.Key, Count = g.Count() }).Where(o => o.Count > 1).ToList();

                foreach (var entry in duplicates1)
                {
                    var ps = (from p in _controller.Db.People
                              where p.Name == entry.Name
                              select p).ToList();
                    foreach (var p in ps)
                    {
                        _controller.UpdatePersonWorks(p);
                    }
                    InvokeOnDebugText(string.Format("Duplicate Person Id: {0}\r\n", entry.Name));
                }


            }
        }


    }
}
